//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler
// FILE:            io.cpp
// AUTHOR:          Vladimir Gumenuk
// DATE:            22-Mar-2008
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"

#ifdef __XVM__
#include "BuildinObjectInfo.h"
#include "XortStringImpl.h"
#define new_string new xstring
#else
#define new_string new string
#endif


STATIC File          g_fLog;
STATIC STRING_ARRAY  g_arERRORS;
STATIC int           g_bVerbose = FALSE;  // compiler generates verbose output during compilation

void SetVerbose( int bVerbose )
{
    g_bVerbose = bVerbose;
}

STATIC void addErrorMessageToTable( string strMsg )
{
#ifdef CPP_COMPILER
    string *str = new_string( strMsg );
#else
    string str;
    str.Assign( strMsg );
#endif
    g_arERRORS.add( str );
}

void InitErrorTable()
{
    addErrorMessageToTable( "INTERNAL COMPILER ERROR - please contact to development team" );        
    addErrorMessageToTable( "undeclared identifier" );                                               
    addErrorMessageToTable( "unexpected token found" );                                              
    addErrorMessageToTable( "unexpected end of file" );                                              
    addErrorMessageToTable( "unknown token found" );                                                 
    addErrorMessageToTable( "unknown type" );                                                        
    addErrorMessageToTable( "impossible conversion" );                                               
    addErrorMessageToTable( "identifier not found" );                                                
    addErrorMessageToTable( "identifier is variable or procedure" );                                 
    addErrorMessageToTable( "declaration syntax error" );                                            
    addErrorMessageToTable( "'const' declaration error" );                                           
    addErrorMessageToTable( "constant expression syntax" );                                          
    addErrorMessageToTable( "expression syntax" );                                                   
    addErrorMessageToTable( "string syntax" );                                                       
    addErrorMessageToTable( "floating point number syntax" );                                        
    addErrorMessageToTable( "modifier applied to declaration" );                                     
    addErrorMessageToTable( "nested procedure is not allowed" );                                     
    addErrorMessageToTable( "operation is no allowed in constant expression" );                      
    addErrorMessageToTable( "operator ++/-- is not supported for complex types" );                   
    addErrorMessageToTable( "operator ++/-- is not supported this type" );                           
    addErrorMessageToTable( "unary '-' is not allowed for this type" );                              
    addErrorMessageToTable( "unary '~' is not allowed for this type" );                              
    addErrorMessageToTable( "unary '!' is not allowed for this type" );                              
    addErrorMessageToTable( "'else' without 'if'" );                                                 
    addErrorMessageToTable( "expected '}'" );                                                        
    addErrorMessageToTable( "expected identifier" );                                                 
    addErrorMessageToTable( "expected constant expression" );                                        
    addErrorMessageToTable( "expected typename" );                                                   
    addErrorMessageToTable( "expected argument name" );                                              
    addErrorMessageToTable( "expected single line statement" );                                      
    addErrorMessageToTable( "expected assignment" );                                                 
    addErrorMessageToTable( "missing '('" );                                                         
    addErrorMessageToTable( "missing ')'" );                                                         
    addErrorMessageToTable( "missing '{'" );                                                         
    addErrorMessageToTable( "missing '}'" );                                                         
    addErrorMessageToTable( "missing ';'" );                                                         
    addErrorMessageToTable( "missing ','" );                                                         
    addErrorMessageToTable( "missing '>'" );                                                         
    addErrorMessageToTable( "missing '\''" );                                                        
    addErrorMessageToTable( "missing 'while'" );                                                     
    addErrorMessageToTable( "identifier is not a variable" );                                        
    addErrorMessageToTable( "void function can not return value" );                                  
    addErrorMessageToTable( "insufficient number of arguments" );                                    
    addErrorMessageToTable( "invalid number of parameters" );                                        
    addErrorMessageToTable( "too many strings that need to be formated" );                           
    addErrorMessageToTable( "methods outside string can not have unspecified number of arguments" ); 
    addErrorMessageToTable( "can not assign to procedure" );                                         
    addErrorMessageToTable( "argument is already declared with different type" );                    
    addErrorMessageToTable( "argument is already declared" );                                        
    addErrorMessageToTable( "forward argument declaration differs" );                                
    addErrorMessageToTable( "function declaration error" );                                          
    addErrorMessageToTable( "constant expression types mismatch" );                                  
    addErrorMessageToTable( "identifier is already declared" );                                      
    addErrorMessageToTable( "function is already declared with different return type" );             
    addErrorMessageToTable( "void function can not be a part of expression" );     
    addErrorMessageToTable( "unary minus applied to unsigned value, result still unsigned" );
    addErrorMessageToTable( "INTERNAL COMPILER ERROR: DECODE INSTRUCTION FAILED!" );
    addErrorMessageToTable( "'default' is already specified" );
    addErrorMessageToTable( "missing ':'" );
    addErrorMessageToTable( "unexpected 'case'" );
    addErrorMessageToTable( "static initializers: non-constant expression used?" ); // ERR_STATIC_INITIALIZERS
    addErrorMessageToTable( "hex digit syntax" ); // ERR_HEXDIGIT_SYNTAX
    addErrorMessageToTable( "enum declaration syntax" ); //ERR_ENUM_SYNTAX
    addErrorMessageToTable( "l-value is constant" ); //ERR_LVALUE_IS_CONSTANT
    addErrorMessageToTable( "object is already declared, second declaration ignored" ); // WRN_SECOND_OBJ_IGNORED
    addErrorMessageToTable( "expected 'break'" ); // ERR_MISSING_BREAK
    addErrorMessageToTable( "expected attribute" ); // ERR_EXPECTED_ATTRIBUTE
    addErrorMessageToTable( "unknown attribute" ); // ERR_UNKNOWN_ATTRIBUTE
    addErrorMessageToTable( "attribute declaration syntax" ); // ERR_ATTRIBUTE_DECL
    addErrorMessageToTable( "function declaration expected" ); // ERR_PROC_DECL_EXPECTED
    addErrorMessageToTable( "function already declared without attributes" ); // ERR_FORWARD_PROC_WITHATTR
    addErrorMessageToTable( "Can not read file" ); // ERR_CANT_READ_FILE
    addErrorMessageToTable( "Invalid platform" ); // ERR_INVALID_PLATFORM
    addErrorMessageToTable( "Type is not allowed in native object declaration" ); // ERR_TYPE_NATIVELY_NOT_ALLOWED
    addErrorMessageToTable( "Type is not allowed in exported object" ); // ERR_TYPE_NOT_ALLOWED_IN_EXPORT_OBJ
}

int io_logOpen( string csFileName )
{
    if( !g_fLog.Open( csFileName, "w" ) )
        return 0;
    return XORT_OK;
}

void io_logClose()
{
    g_fLog.Close();
}

#ifdef CPP_COMPILER
#ifdef BUILD_FOR_PC
#include <windows.h>
#endif
void message( const char *lpszMsg, ... )
{
    if( g_fLog.isOpened() )
    {
        BYTE_ARRAY buf;
        if( buf.reserve( 1024 ) )
        {
            int iFmt;
            va_list argptr;
            va_start( argptr, lpszMsg );
            iFmt = vsnprintf( (char*)buf.ptr(0), 1024, lpszMsg, argptr );
            va_end( argptr );
            buf.set_itemsCount( iFmt );
            buf.add( '\r' );
            buf.add( '\n' );
            g_fLog.WriteByteArray( buf );
#ifdef BUILD_FOR_PC
            OutputDebugStringA( (char*)buf.ptr(0) );
#endif
            //printf( "%s\n", (char*)buf.ptr(0) );
        }   
    }
}

void DbgPrint( const char *lpszFmt, ... )
{
    // DO NOTHING in native version.
}

void OutMsg( const char *lpszMsg, ... )
{
    // DO NOTHING
}

#else
void message( string lpszMsg, ... )
{
    if( g_fLog.isOpened() )
    {
        string csOut; csOut.Assign( lpszMsg );

        dword dwNumArgs = __get_arg_count();

        dword dwArgTest = x_isfmtstr( lpszMsg );
        if( dwArgTest != ( dwNumArgs-1 ) )
            return; // ERR_INVALID_ARG_COUNT; !!!-ASSERT-!!!

        if( dwNumArgs > 1 ) // 0 arg is lpszMsg...
        {
            dword i; 
            for( i = 1; i < dwNumArgs; ++i )
            {
                XortElement arg = __get_arg( i );
                string csFmt;

                switch( arg.bType )
                {
                case t_v:  csFmt.Format( "%s", "error: void arg!" ); break;  // in case of error uninitialized object is returned, since no exception mechanism //return ERR_INVALID_TYPE;
                case t_s1: csFmt.Format( "%c",  arg._s1 ); break;
                case t_u1: csFmt.Format( "%02X",arg._u1 ); break;
                case t_s2: csFmt.Format( "%d",  arg._s2 ); break;
                case t_u2: csFmt.Format( "%04X",arg._u2 ); break;
                case t_s4: csFmt.Format( "%d",  arg._s4 ); break;
                case t_u4: csFmt.Format( "%08X",  arg._u4 ); break;
                case t_s8: csFmt.Format( "%lld", arg._s8 );break;
                case t_u8: csFmt.Format( "%llu", arg._u8 );break; // %016I64X
                case t_r4: csFmt.Format( "%.6f", arg._r4 ); break;
                case t_r8: csFmt.Format( "%.15f", arg._r8 );break;
                case t_p:  csFmt.Format( "%s", arg._p );    break;
                default:   csFmt.Assign( "error: unknown arg type!" ); break;  // in case of error uninitialized object is returned, since no exception mechanism
                }

                csOut.Replace( "%@", csFmt, 1 );
            }
        }
        
        //csOut.Add( "\r\n" ); - TODO: fixme - bug in handling of escape sequences
        csOut.Append( 13B ); csOut.Append( 10B );
        
        g_fLog.WriteString( csOut );
        DbgPrint( csOut );
    }
}

void OutMsg( string lpszMsg, ... )
{
    if( g_bVerbose )
    {
        // do not have __ARGS__ yet, so this is just a copy of message...
        
        string csOut; csOut.Assign( lpszMsg );

        dword dwNumArgs = __get_arg_count();

        dword dwArgTest = x_isfmtstr( lpszMsg );
        if( dwArgTest != (dwNumArgs - 1) )
            return; // ERR_INVALID_ARG_COUNT; !!!-ASSERT-!!!

        if( dwNumArgs > 1 ) // 0 arg is lpszMsg...
        {
            dword i;
            for( i = 1; i < dwNumArgs; ++i )
            {
                XortElement arg = __get_arg( i );
                string csFmt;

                switch( arg.bType )
                {
                case t_v:  csFmt.Format( "%s", "error: void arg!" ); break;  // in case of error uninitialized object is returned, since no exception mechanism //return ERR_INVALID_TYPE;
                case t_s1: csFmt.Format( "%c", arg._s1 ); break;
                case t_u1: csFmt.Format( "%02X", arg._u1 ); break;
                case t_s2: csFmt.Format( "%d", arg._s2 ); break;
                case t_u2: csFmt.Format( "%04X", arg._u2 ); break;
                case t_s4: csFmt.Format( "%d", arg._s4 ); break;
                case t_u4: csFmt.Format( "%08X", arg._u4 ); break;
                case t_s8: csFmt.Format( "%lld", arg._s8 ); break;
                case t_u8: csFmt.Format( "%llu", arg._u8 ); break; // %016I64X
                case t_r4: csFmt.Format( "%.6f", arg._r4 ); break;
                case t_r8: csFmt.Format( "%.15f", arg._r8 ); break;
                case t_p:  csFmt.Format( "%s", arg._p );    break;
                default:   csFmt.Assign( "error: unknown arg type!" ); break;  // in case of error uninitialized object is returned, since no exception mechanism
                }

                csOut.Replace( "%@", csFmt, 1 );
            }
        }

        //csOut.Add( "\r\n" ); - TODO: fixme - bug in handling of escape sequences
        csOut.Append( 13B ); csOut.Append( 10B );

        printf( csOut );
    }
}

#endif

void x_error( dword dwErrorCode, dword dwLine, dword dwPos )
{
    string csError, csCode, csLine, csPos, csCodeLine;
    dword i;

    csCodeLine = ( dwLine ) ? lex_GetLine( dwLine ) : "no line information";
    csCodeLine.Add( "\r\n" );

    csCode.dword2Str( dwErrorCode );
    csLine.dword2Str( dwLine+1 ); // IDE counts from 1, cl - from 0
    csPos.dword2Str( dwPos );
    
    csError = lex_getFileName();
    csError.Append( '(' );
    csError.Add( csLine );
    csError.Append( ')' );
    if( dwErrorCode == WRN_SECOND_OBJ_IGNORED ) // this is only 1 warnning produced by xcl
        csError.Add( " : warning [" );
    else
        csError.Add( " : error [" );
    csError.Add( csLine );
    csError.Append( ':' );
    csError.Add( csPos );
    csError.Add( "] " );
#ifdef CPP_COMPILER
    stringref pcsErr = g_arERRORS.get( dwErrorCode );
    csError.Add( *pcsErr );
#else
    string pcsErr = g_arERRORS.get( dwErrorCode );
    csError.Add( pcsErr );
#endif
    csError.Add( "\r\n" );

    g_fLog.WriteString( csError );
    g_fLog.WriteString( csCodeLine );
#ifdef CPP_COMPILER
    fputs( csError.GetBuffer(), stderr );
    fputs( csCodeLine.GetBuffer(), stderr );
#ifdef BUILD_FOR_PC
    OutputDebugStringA( csError.GetBuffer() );
    OutputDebugStringA( csCodeLine.GetBuffer() );
#endif
#endif
    csCodeLine.Empty();

    if( dwPos > 0 ) {
        dwPos = dwPos - 1;
    }

    for( i = 0; i < dwPos; ++i ) {
        csCodeLine.Append( '~' );
    }
    csCodeLine.Add( "^\r\n" );
    g_fLog.WriteString( csCodeLine );
#ifdef CPP_COMPILER
    fprintf( stderr, csCodeLine.GetBuffer() );
#ifdef BUILD_FOR_PC
    OutputDebugStringA( csCodeLine.GetBuffer() );
#endif
#endif
}